/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "ModUtil.h"
#include "RNOH/arkui/NativeNodeApi.h"
#include "ClipPathViewNoneNode.h"
#include "SVGPathParser.h"
#include "SVGViewBox.h"
#include "pen_style_node.h"
#include <native_drawing/drawing_brush.h>
#include <native_drawing/drawing_canvas.h>
#include <native_drawing/drawing_color.h>
#include <native_drawing/drawing_path.h>
#include <native_drawing/drawing_pen.h>
#include <native_drawing/drawing_point.h>
#include <native_drawing/drawing_rect.h>
#include <native_drawing/drawing_shader_effect.h>
#include <native_drawing/drawing_types.h>

namespace rnoh {
ClipPathViewNoneNode::ClipPathViewNoneNode()
    : ArkUINode(NativeNodeApi::getInstance()->createNode(ArkUI_NodeType::ARKUI_NODE_CUSTOM))
{
    canvasCallback_ = new CanvasCallback();
    canvasCallback_->callback = [this](ArkUI_NodeCustomEvent *event) {
        auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
        switch (type) {
            case ARKUI_NODE_CUSTOM_EVENT_ON_FOREGROUND_DRAW:
                OnDraw(event);
                break;
            case ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE: {
                int32_t width = getSavedWidth();
                int32_t height = getSavedHeight();
                maybeThrow(NativeNodeApi::getInstance()->setMeasuredSize(m_nodeHandle, width, height));
                setRectF(mBounds, 0, 0, width, height);
            } break;
            default:
                break;
        }
    };
    eventReceiver = [](ArkUI_NodeCustomEvent *event) {
        int32_t targetId = OH_ArkUI_NodeCustomEvent_GetEventTargetId(event);
        int count10001 = 10001;
        int count10002 = 10002;
        if (targetId == count10001 || targetId == count10002) {
            auto *userData = reinterpret_cast<CanvasCallback *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
            if (userData != nullptr && userData->callback != nullptr) {
                userData->callback(event);
            }
        }
    };
    int count101 = 10001;
    int count102 = 10002;
    maybeThrow(NativeNodeApi::getInstance()->addNodeCustomEventReceiver(m_nodeHandle, eventReceiver));
    maybeThrow(NativeNodeApi::getInstance()->registerNodeCustomEvent(m_nodeHandle,
        ARKUI_NODE_CUSTOM_EVENT_ON_FOREGROUND_DRAW, count101, canvasCallback_));
    maybeThrow(NativeNodeApi::getInstance()->registerNodeCustomEvent(m_nodeHandle, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE,
        count102, canvasCallback_));
    OH_Drawing_PenIsAntiAlias(mPaint);
    OH_Drawing_PenIsAntiAlias(mPaintStroke);
    OH_Drawing_PenSetColor(mPaint, 0xffffffff);
    OH_Drawing_PenSetColor(mPaintStroke, 0xffffffff);
}

ClipPathViewNoneNode::~ClipPathViewNoneNode()
{
    NativeNodeApi::getInstance()->removeNodeCustomEventReceiver(m_nodeHandle, eventReceiver);
    NativeNodeApi::getInstance()->unregisterNodeCustomEvent(m_nodeHandle, ARKUI_NODE_CUSTOM_EVENT_ON_FOREGROUND_DRAW);
    NativeNodeApi::getInstance()->unregisterNodeCustomEvent(m_nodeHandle, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE);
    OH_Drawing_RectDestroy(mRectVb);
    OH_Drawing_RectDestroy(mRectVbDensity);
    OH_Drawing_RectDestroy(mBounds);
    OH_Drawing_RectDestroy(mRectPath);
    OH_Drawing_PathDestroy(mPath);
    OH_Drawing_PathDestroy(mPath2);
    OH_Drawing_MatrixDestroy(mMatrix);
    OH_Drawing_PenDestroy(mPaint);
    OH_Drawing_PenDestroy(mPaintStroke);
    delete canvasCallback_;
    delete mProps;
    canvasCallback_ = nullptr;
    mProps = nullptr;
    canvas = nullptr;
}

void ClipPathViewNoneNode::InsertChild(ArkUINode &child, std::size_t index)
{
    maybeThrow(NativeNodeApi::getInstance()->insertChildAt(m_nodeHandle, child.getArkUINodeHandle(), index));
}

void ClipPathViewNoneNode::RemoveChild(ArkUINode &child)
{
    maybeThrow(NativeNodeApi::getInstance()->removeChild(m_nodeHandle, child.getArkUINodeHandle()));
}

void ClipPathViewNoneNode::setPanStyleMode(OH_Drawing_Canvas *canvas, int type)
{
    auto cBrush = OH_Drawing_BrushCreate();
    switch (type) {
        case CLIP_PEN_STYLE_FILL:
            OH_Drawing_BrushSetColor(cBrush, OH_Drawing_ColorSetArgb(0xff, 0xff, 0xff, 0xff));
            break;
        case CLIP_PEN_STYLE_STROKE:
            OH_Drawing_BrushSetColor(cBrush, OH_Drawing_ColorSetArgb(0x00, 0x00, 0x00, 0x00));
            break;
        default:
            break;
    }
    OH_Drawing_CanvasAttachBrush(canvas, cBrush);
    OH_Drawing_BrushDestroy(cBrush);
}

void ClipPathViewNoneNode::OnDraw(ArkUI_NodeCustomEvent *event)
{
    auto *drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
    canvas = reinterpret_cast<OH_Drawing_Canvas *>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
    auto width_ = OH_Drawing_CanvasGetWidth(canvas);
    auto height_ = OH_Drawing_CanvasGetHeight(canvas);

    auto canvasRect = OH_Drawing_RectCreate(0.0f, 0.0f, width_, height_);
    OH_Drawing_CanvasSaveLayer(canvas, canvasRect, nullptr);
    OH_Drawing_Brush *cBrush_back = OH_Drawing_BrushCreate();
    OH_Drawing_BrushSetColor(cBrush_back, OH_Drawing_ColorSetArgb(0xff, 0xff, 0xff, 0xff));
    OH_Drawing_CanvasDrawBackground(canvas, cBrush_back);

    OH_Drawing_Brush *cBrush_ = OH_Drawing_BrushCreate();
    OH_Drawing_BrushSetBlendMode(cBrush_, BLEND_MODE_DST_OUT);
    OH_Drawing_CanvasSaveLayer(canvas, canvasRect, cBrush_);

    drawPath(canvas);
    OH_Drawing_RectDestroy(canvasRect);
    OH_Drawing_BrushDestroy(cBrush_back);
    OH_Drawing_BrushDestroy(cBrush_);
}

void ClipPathViewNoneNode::drawPath(OH_Drawing_Canvas *canvas)
{
    setupPath(canvas);
    viewBoxTransform();
    setPaintStrokeProps();
    canvasTransform();
    OH_Drawing_CanvasSave(canvas);
    OH_Drawing_CanvasConcatMatrix(canvas, mMatrix);

    uint32_t count = OH_Drawing_CanvasGetSaveCount(canvas);
    OH_Drawing_CanvasAttachPen(canvas, mPaint);
    setPanStyleMode(canvas, CLIP_PEN_STYLE_FILL);
    OH_Drawing_CanvasDrawPath(canvas, mPath);
    OH_Drawing_CanvasDetachPen(canvas);

    OH_Drawing_CanvasAttachPen(canvas, mPaintStroke);
    setPanStyleMode(canvas, CLIP_PEN_STYLE_STROKE);
    OH_Drawing_CanvasDrawPath(canvas, mPath2);
    OH_Drawing_CanvasDetachPen(canvas);

    OH_Drawing_CanvasRestoreToCount(canvas, count);
    OH_Drawing_CanvasRestore(canvas);
    OH_Drawing_CanvasRestore(canvas);
}

void ClipPathViewNoneNode::setupPath(OH_Drawing_Canvas *canvas)
{
    if (!canvas || mD.empty()) {
        return;
    }
    if (mPath) {
        OH_Drawing_PathReset(mPath);
    }
    SVGPathParser::mScale = ModUtil::getDensity();
    mPath = SVGPathParser::parse(mD);
}
void ClipPathViewNoneNode::SetD(const std::string d)
{
    mD = d;
    markDirty();
}
void ClipPathViewNoneNode::SetStrokeWidth(float const width)
{
    mProps->mStrokeWidth = width;
}
void ClipPathViewNoneNode::SetStrokeCap(std::string const cap)
{
    mProps->mStrokeCap = cap;
}
void ClipPathViewNoneNode::SetStrokeJoin(std::string const join)
{
    mProps->mStrokeJoin = join;
}
void ClipPathViewNoneNode::SetStrokeMiter(float const miter)
{
    mProps->mStrokeMiter = miter;
}
void ClipPathViewNoneNode::SetStrokeStart(float const start)
{
    mProps->mStrokeStart = start;
}
void ClipPathViewNoneNode::SetStrokeEnd(float const end)
{
    mProps->mStrokeEnd = end;
}
float ClipPathViewNoneNode::toDip(float value)
{
    return value * ModUtil::getDensity();
}
void ClipPathViewNoneNode::SetFillRule(std::string v)
{
    mProps->mFillRule = v.empty() ? "none" : v;
}
void ClipPathViewNoneNode::SetAlign(std::string v)
{
    mAlign = v.empty() ? "xMidYMid" : v;
}
void ClipPathViewNoneNode::SetAspect(std::string v)
{
    mAspect = v.empty() ?
        SVGViewBox::MOS_MEET :
        (v.compare("slice") == 0 ? SVGViewBox::MOS_SLICE :
                                   (v.compare("none") == 0 ? SVGViewBox::MOS_NONE : SVGViewBox::MOS_MEET));
}
void ClipPathViewNoneNode::setRect(OH_Drawing_Rect *target, OH_Drawing_Rect *src)
{
    OH_Drawing_RectSetLeft(target, OH_Drawing_RectGetLeft(src));
    OH_Drawing_RectSetTop(target, OH_Drawing_RectGetTop(src));
    OH_Drawing_RectSetRight(target, OH_Drawing_RectGetRight(src));
    OH_Drawing_RectSetBottom(target, OH_Drawing_RectGetBottom(src));
}

void ClipPathViewNoneNode::setRectF(OH_Drawing_Rect *target, float left, float top, float right, float bottom)
{
    setRect(target, OH_Drawing_RectCreate(left, top, right, bottom));
}

void ClipPathViewNoneNode::SetTranslateZ(float z)
{
    ArkUI_NumberValue translateValue[] = {{.f32 = z}};
    ArkUI_AttributeItem translateItem = { translateValue, sizeof(translateValue) / sizeof(ArkUI_NumberValue) };
    uint32_t code = NativeNodeApi::getInstance()->setAttribute(m_nodeHandle, NODE_Z_INDEX, &translateItem);
}
void ClipPathViewNoneNode::SetTransX(float v)
{
    if (mTranslationX != v) {
        mTranslationX = v;
    }
}
void ClipPathViewNoneNode::SetTransY(float v)
{
    if (mTranslationY != v) {
        mTranslationY = v;
    }
}
void ClipPathViewNoneNode::SetTransPercentageValue(bool v)
{
    if (mTranslationIsPercent != v) {
        mTranslationIsPercent = v;
    }
}

void ClipPathViewNoneNode::SetRot(float v)
{
    if (mRotation != v) {
        mRotation = v;
    }
}
void ClipPathViewNoneNode::SetRotO(float v)
{
    if (mRotationOx != v || mRotationOy != v) {
        mRotationOx = v;
        mRotationOy = v;
    }
}
void ClipPathViewNoneNode::SetRotOx(float v)
{
    if (mRotationOx != v) {
        mRotationOx = v;
    }
}
void ClipPathViewNoneNode::SetRotOy(float v)
{
    if (mRotationOy != v) {
        mRotationOy = v;
    }
}
void ClipPathViewNoneNode::SetRotPercentageValue(bool v)
{
    if (mRotationIsPercent != v) {
        mRotationIsPercent = v;
    }
}
void ClipPathViewNoneNode::SetSc(float v)
{
    if (mScaleX != v || mScaleY != v) {
        mScaleX = v;
        mScaleY = v;
    }
}
void ClipPathViewNoneNode::SetScX(float v)
{
    if (mScaleX != v) {
        mScaleX = v;
    }
}

void ClipPathViewNoneNode::SetScY(float v)
{
    if (mScaleY != v) {
        mScaleY = v;
    }
}

void ClipPathViewNoneNode::SetScO(float v)
{
    if (mScaleOriginX != v || mScaleOriginY != v) {
        mScaleOriginX = v;
        mScaleOriginY = v;
    }
}
void ClipPathViewNoneNode::SetScOx(float v)
{
    if (mScaleOriginX != v) {
        mScaleOriginX = v;
    }
}
void ClipPathViewNoneNode::SetScOy(float v)
{
    if (mScaleOriginY != v) {
        mScaleOriginY = v;
    }
}
void ClipPathViewNoneNode::SetScPercentageValue(bool v)
{
    if (mScaleIsPercent != v) {
        mScaleIsPercent = v;
    }
}

void ClipPathViewNoneNode::SetViewBox(std::vector<float> viewBox)
{
    if (viewBox.empty()) {
        return;
    }
    float density = ModUtil::getDensity();
    int count0 = 0;
    int count1 = 1;
    int count2 = 2;
    int count3 = 3;
    setRectF(mRectVb, viewBox[count0], viewBox[count1], (viewBox[count0] + viewBox[count2]),
            (viewBox[count1] + viewBox[count3]));
    setRectF(mRectVbDensity, viewBox[count0] * density, viewBox[count1] * density,
            (viewBox[count0] + viewBox[count2]) * density, (viewBox[count1] + viewBox[count3]) * density);
}

void ClipPathViewNoneNode::viewBoxTransform()
{
    float density = ModUtil::getDensity();
    if (validateViewBox()) {
        setRect(mRectPath, mRectVbDensity);
        SVGViewBox::transform(mRectVb, mBounds, mAlign, mAspect, density, mMatrix);
        OH_Drawing_PathTransform(mPath, mMatrix);
        OH_Drawing_MatrixMapRect(mMatrix, mRectPath, mRectPath);
    } else {
        setRect(mRectPath, mBounds);
    }
}
bool ClipPathViewNoneNode::validateViewBox()
{
    return OH_Drawing_RectGetWidth(mRectVb) >= 0.0f && OH_Drawing_RectGetHeight(mRectVb) >= 0.0f;
}

void ClipPathViewNoneNode::setPaintStrokeProps()
{
    OH_Drawing_PathSetFillType(mPath, mProps->getFillRule());
    float sw = validateViewBox() ? ModUtil::viewBoxToMax(mProps->getStrokeWidth(), mRectVb,
        OH_Drawing_RectGetWidth(mRectPath), OH_Drawing_RectGetHeight(mRectPath)) :
                                   toDip(mProps->getStrokeWidth());
    OH_Drawing_PenSetWidth(mPaintStroke, sw);
    OH_Drawing_PenSetCap(mPaintStroke, mProps->getStrokeCap());
    OH_Drawing_PenSetJoin(mPaintStroke, mProps->getStrokeJoin());
    OH_Drawing_PenSetMiterLimit(mPaintStroke, mProps->getStrokeMiter());
    OH_Drawing_PathReset(mPath2);
    mPath2 = OH_Drawing_PathCopy(mPath);
    if (mProps->getStrokeStart() != 0.0f || mProps->getStrokeEnd() != 1.0f) {
        OH_Drawing_PathMoveTo(mPath2, OH_Drawing_PathGetLength(mPath2, false) * mProps->getStrokeStart(),
            OH_Drawing_PathGetLength(mPath2, false) * mProps->getStrokeEnd());
        OH_Drawing_PathLineTo(mPath2, 0.0f, 0.0f);
    }
}

void ClipPathViewNoneNode::canvasTransform()
{
    OH_Drawing_MatrixReset(mMatrix);
    if (mRotation != 0.0f) {
        float rotX;
        float rotY;
        if (mRotationIsPercent) {
            rotX = (mRotationOx * getSavedWidth());
            rotY = (mRotationOy * getSavedHeight());
        } else if (validateViewBox()) {
            rotX = OH_Drawing_RectGetLeft(mRectPath) +
                ModUtil::viewBoxToWidth(mRotationOx, mRectVb, OH_Drawing_RectGetWidth(mRectPath));
            rotY = OH_Drawing_RectGetTop(mRectPath) +
                ModUtil::viewBoxToHeight(mRotationOy, mRectVb, OH_Drawing_RectGetHeight(mRectPath));
        } else {
            rotX = toDip(mRotationOx);
            rotY = toDip(mRotationOy);
        }
        OH_Drawing_MatrixPostRotate(mMatrix, mRotation, rotX, rotY);
    }

    if (mScaleX != 1.0f || mScaleY != 1.0f) {
        float oX;
        float oY;
        if (mScaleIsPercent) {
            oX = (mScaleOriginX * getSavedWidth());
            oY = (mScaleOriginY * getSavedHeight());
        } else if (validateViewBox()) {
            oX = OH_Drawing_RectGetLeft(mRectPath) +
                ModUtil::viewBoxToWidth(mScaleOriginX, mRectVb, OH_Drawing_RectGetWidth(mRectPath));
            oY = OH_Drawing_RectGetTop(mRectPath) +
                ModUtil::viewBoxToHeight(mScaleOriginY, mRectVb, OH_Drawing_RectGetHeight(mRectPath));
        } else {
            oX = toDip(mScaleOriginX);
            oY = toDip(mScaleOriginY);
        }
        OH_Drawing_MatrixPostScale(mMatrix, mScaleX, mScaleY, oX, oY);
    }

    if (mTranslationX != 0.0f || mTranslationY != 0.0f) {
        float transX;
        float transY;
        if (mTranslationIsPercent) {
            transX = (mTranslationX * getSavedWidth());
            transY = (mTranslationY * getSavedHeight());
        } else if (validateViewBox()) {
            transX = (mTranslationX / OH_Drawing_RectGetWidth(mRectVb)) * OH_Drawing_RectGetWidth(mRectPath);
            transY = (mTranslationY / OH_Drawing_RectGetHeight(mRectVb)) * OH_Drawing_RectGetHeight(mRectPath);
        } else {
            transX = toDip(mTranslationX);
            transY = toDip(mTranslationY);
        }
        OH_Drawing_MatrixPostTranslate(mMatrix, transX, transY);
    }
}
} // namespace rnoh